package org.hdl.anima;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.hdl.anima.backend.BackendServer;
import org.hdl.anima.blacklist.BlackListMgr;
import org.hdl.anima.common.module.Module;
import org.hdl.anima.filter.HandlerFilter;
import org.hdl.anima.fronent.FronetServer;
import org.hdl.anima.handler.ServiceHandlers;
import org.hdl.anima.proxy.ServerProxys;
import org.hdl.anima.route.RouteTable;
import org.hdl.anima.route.Router;
import org.hdl.anima.session.BackendSessionMgr;
import org.hdl.anima.session.ClientSessionMgr;
import org.hdl.anima.transport.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;

/**
 * 
 * @author qiuhd
 * @since  2014-8-1
 * @version V1.0.0
 */
public class Application extends AbstractApplication{
	
	private final static Logger LOGGER = LoggerFactory.getLogger(Application.class);
	private AppConf conf = new AppConf();
	private final Version version = new Version(0, 0, 1);
	private final ClassLoader classLoader ;
	protected ServerConifg serverConfig;
    private Map<Class<?>, Module> modules = new LinkedHashMap<Class<?>, Module>();
    private final List<HandlerFilter> beforeFilters = new ArrayList<HandlerFilter>();
    private final List<HandlerFilter> afterFilters = new ArrayList<HandlerFilter>();
    
    private volatile boolean start = false;
    
	public static Application create() {
		return new Application() ;
	}
	
	public Application() {
		this.classLoader = Thread.currentThread().getContextClassLoader();
		try {
			AppHelper.loadFromStaticXml(this);
			LOGGER.info(
					"Starting to server...........................");
			init();
			LOGGER.info(
					"Succeed in starting the server: ServerId {}, Host {}, Port {}, Frontent {}",
					serverConfig.getId(), serverConfig.getHost(),
					serverConfig.getPort(), serverConfig.isFrontend());
		}catch(Throwable e) {
			e.printStackTrace();
			LOGGER.error("Failed to start server,Cause :" + e.getMessage(),e);
			System.exit(1);
		}
	}
	
	private void initAppConfig() {
		conf.set(Constants.BIND_HOST, serverConfig.getHost());
		conf.setInt(Constants.BIND_PORT, serverConfig.getPort());
	}
	
	private void init() throws Exception {
		initAppConfig();
		loadModules();
		initMoudles();
		startModules();
		start = true;
	}
	
	/**
	 * Load all modules
	 */
	private void loadModules() {
		if (serverConfig.isFrontend()) {
			loadModule(BlackListMgr.class.getName());
			loadModule(ServiceHandlers.class.getName());
			loadModule(ServerProxys.class.getName());
			loadModule(Router.class.getName());
			loadModule(RouteTable.class.getName());
			loadModule(ClientSessionMgr.class.getName());
			loadModule(FronetServer.class.getName());
		} else {
			loadModule(ServiceHandlers.class.getName());
			loadModule(BackendSessionMgr.class.getName());
			loadModule(BackendServer.class.getName());
		}
	}
	
	/**
     * Loads a module.
     *
     */
    private void loadModule(String module) {
        try {
            Class<?> modClass = classLoader.loadClass(module);
            Constructor<?> constructor = modClass.getConstructor(String.class);
            Module mod = (Module) constructor.newInstance(module);
            this.modules.put(modClass, mod);
        }
        catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("Failed to load module,module name {}.cause :{}",module,e.getMessage(),e);
        }
    }
    
	private void initMoudles() {
		for (Module module : modules.values()) {
			try {
				module.initialize(this);
			} catch (Exception e) {
				e.printStackTrace();
	            LOGGER.error("Failed to init module,module name {}.cause :{}",module,e.getMessage(),e);
			}
		}
	}

	private void startModules() throws Exception {
		for (Module module : modules.values()) {
			try {
				module.start();
			} catch (Exception e) {
				e.printStackTrace();
				LOGGER.error("Failed to start module,module name {}.cause :{}",module,e.getMessage(),e);
				throw e;
			}
		}
	}
	
	public void stop() {
		for (Module module : modules.values()) {
			try {
				module.stop();
			} catch (Exception e) {
				e.printStackTrace();
	            LOGGER.error("Failed to stop module,module name {}.cause :{}",module,e.getMessage(),e);
			}
		}
	}
	
	public void destory() {
		stop();
		for (Module module : modules.values()) {
			try {
				module.destroy();
			} catch (Exception e) {
				e.printStackTrace();
	            LOGGER.error("Failed to destory module,module name {}.cause :{}",module,e.getMessage(),e);
			}
		}
	}

	@Override
	public AppConf getAppConf() {
		return conf;
	}

	@Override
	public String getId() {
		return serverConfig.getId();
	}

	@Override
	public Version getVersion() {
		return version;
	}

	@Override
	public boolean isFrontend() {
		return serverConfig.isFrontend();
	}

	@Override
	public void addBefore(HandlerFilter filter) {
		Preconditions.checkArgument(filter != null,"filter is null");
		this.beforeFilters.add(filter);
	}
	
	@Override
	public void addAfter(HandlerFilter filter) {
		Preconditions.checkArgument(filter != null,"filter is null");
		this.beforeFilters.add(filter);
	}

	@Override
	public List<HandlerFilter> getBefore() {
		return Collections.unmodifiableList(this.beforeFilters);
	}

	@Override
	public List<HandlerFilter> getAfter() {
		return Collections.unmodifiableList(this.afterFilters);
	}

	@Override
	@SuppressWarnings("unchecked")
	public <T> T getMoulde(Class<T> clazz) {
		return (T) modules.get(clazz);
	}

	@Override
	public ClassLoader getClassLoader() {
		return classLoader;
	}
}

